package com.qire.antsrouter.card;

import android.app.Activity;
import android.os.Handler;

import com.qire.antscore.common.AssertUtils;
import com.qire.antscore.entity.RouteMeta;
import com.qire.antsrouter.AntsRouter;
import com.qire.antsrouter.lifecycleHandler.ActivityManageHandler;

import java.lang.reflect.InvocationTargetException;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

/**
 * 房卡（Fragment跳卡）
 */
public class RoomCard extends Postcard<Fragment> {

    private Fragment fragment;

    protected RoomCard(RouteMeta routeMeta, Handler handler) {
        super(routeMeta, handler);
    }

    /**
     * 注入数据
     * @param fieldName 注入字段名
     * @param data      注入数据
     * @return 这张房卡
     */
    public RoomCard withData(String fieldName, Object data) {
        dataContainer.put(fieldName, data);
        return this;
    }

    /**
     * 注入，目前与FloorCard行为一致，可以考虑归入基类
     * @param fragment 注入目标
     * @param <T> 注入目标的类型
     */
    @Override
    public <T extends Fragment> void inject(T fragment) {
        super.inject(fragment);
    }

    /**
     * 导航查找出 {@code Fragment} 并注入相关数据。
     * @return 返回带有 {@code Fragment} 的 RoomCard
     */
    @Override
    public RoomCard navigation() {
        try {
            fragment = routeMeta.<Fragment>getDestination().getConstructor().newInstance();
            inject(fragment);
        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
        return this;
    }

    /**
     * 返回 {@code RoomCard} 描述的 {@link Fragment}
     * @return 存在则直接返回，不存在则导航出 {@link Fragment} 在返回
     */
    public Fragment get() {
        if(fragment == null) {
            navigation();
        }
        return fragment;
    }

    /**
     * 跳转到对应 {@code path} 描述的 {@link Activity}，并且显示当前 {@link Fragment} 到 {@code containerViewId} 所指的View中。
     * 如果 {@code path} 描述的 {@link Activity} 已经被打开，则跳转，如果不存在则导航到具体页面并打开
     * @param path 目标 {@code path}
     * @param containerViewId {@code Activity} 中用于展示 {@link Fragment} 的容器View的ID
     */
    public void to(String path, String group, int containerViewId) {
        to(AntsRouter.ROUTER.buildCard(path, group), containerViewId);
    }

    /**
     * 跳转到对应 {@link FloorCard} 描述的 {@link Activity}，并且显示当前 {@link Fragment} 到 {@code containerViewId} 所指的View中。
     * 如果 {@link FloorCard} 描述的 {@link Activity} 已经被打开，则跳转，如果不存在则导航到具体页面并打开
     * @param floorCard 目标 {@code FloorCard}
     * @param containerViewId {@code Activity} 中用于展示 {@link Fragment} 的容器View的ID
     */
    public void to(FloorCard floorCard, int containerViewId) {
        Class activityClass = floorCard.routeMeta.getDestination();
        Activity activity = ActivityManageHandler.HANDLER.findActivityBy(activityClass);
        if(null == activity) {
            floorCard.navigation();
            return;
        }
        to(activity, containerViewId);
    }

    /**
     * 在此提供一个默认处理 {@link Activity} 的方案 {@link #onToActivity} 来处理跳转，
     * 如果目标是 {@link FragmentActivity} 则使用 {@link #onToFragmentActivity} 来处理跳转，
     * 因为一些特殊的派生 {@link Activity} 有不同管理 {@link Fragment} 的方案，故区分处理，
     * 目前没有处理其他的派生 {@link Activity} 如果有需要特殊处理的，可以修改此方法来增加扩展兼容。
     * @param activity  目标 {@code Activity}
     * @param containerViewId   {@code Activity} 中用于展示 {@link Fragment} 的容器View的ID
     */
    public void to(Activity activity, int containerViewId) {
        if(null == activity) {
            return;
        }
        if(activity instanceof FragmentActivity) {
            onToFragmentActivity((FragmentActivity) activity, containerViewId);
        } else {
            onToActivity(activity, containerViewId);
        }
    }

    /**
     * 处理跳转 {@link Activity} 具体实现方案，跳转应该始终由 {@link #to(Activity, int)} 方法进行统一处理并决定派发给那个方法处理
     * @param activity          目标 {@code activity}
     * @param containerViewId   接受 {@link Fragment} 的容器ViewID，它应该存在于 {@code activity} 的布局中
     */
    private void onToActivity(Activity activity, int containerViewId) {
        android.app.FragmentManager fragmentManager = activity.getFragmentManager();
        android.app.Fragment saveFragment = fragmentManager.findFragmentByTag(routeMeta.getPath());
        fragmentManager.beginTransaction().add(containerViewId, saveFragment, routeMeta.getPath());
    }

    /**
     * 处理跳转 {@link FragmentActivity} 具体实现方案，跳转应该始终由 {@link #to(Activity, int)} 方法进行统一处理并决定派发给那个方法处理
     * @param activity          目标 {@code activity}
     * @param containerViewId   接受 {@link Fragment} 的容器ViewID，它应该存在于 {@code activity} 的布局中
     */
    private void onToFragmentActivity(FragmentActivity activity, int containerViewId) {
        FragmentManager fragmentManager = activity.getSupportFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

        Fragment saveFragment = fragmentManager.findFragmentByTag(routeMeta.getPath());
        Fragment oldFragment = getCurrentFragment(fragmentManager);

        if(oldFragment != null) {
            fragmentTransaction.hide(oldFragment);
        }

        if(saveFragment == null) {
            navigation();
            fragmentTransaction.add(containerViewId, fragment, routeMeta.getPath());
        } else {
            if(AssertUtils.notEmpty(dataContainer)) {
                inject(saveFragment);
            }
//            fragmentTransaction.add(containerViewId, saveFragment, routeMeta.getPath())
//            fragmentTransaction.replace(containerViewId, saveFragment, routeMeta.getPath())
            fragmentTransaction.show(saveFragment);
        }

        if(fragmentManager.isStateSaved()) {
            fragmentTransaction.commitAllowingStateLoss();
        } else {
            fragmentTransaction.commit();
        }
    }

    /**
     * 从 {@link FragmentManager} 中获得正在显示的 {@link Fragment}
     * @param fragmentManager Fragment管理器
     * @return 管理器中正在显示的 {@link Fragment}
     */
    private Fragment getCurrentFragment(FragmentManager fragmentManager) {
        for(Fragment fragment : fragmentManager.getFragments()) {
            if(fragment.isVisible()) {
                return fragment;
            }
        }
        return null;
    }

}
